package com.zhenwei.zscm.common.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.BooleanUtils;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.zhenwei.zscm.common.base.RestMsg;
import com.zhenwei.zscm.common.excel.listener.DefaultListener;
import com.zhenwei.zscm.common.excel.listener.MergeCellStrategyHandler;
import com.zhenwei.zscm.common.excel.model.ExcelBaseModel;
import com.zhenwei.zscm.common.excel.model.ExcelHeadModel;
import com.zhenwei.zscm.common.excel.model.ExcelMergeModel;
import com.zhenwei.zscm.common.excel.model.ExcelUnmergeIgnored;
import com.zhenwei.zscm.common.exception.CustomException;
import com.zhenwei.zscm.common.utils.Linq;
import com.zhenwei.zscm.config.SystemConfig;
import com.zhenwei.zscm.model.enums.TemplateEnum;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Getter
@Slf4j
public class ExcelManager<T extends ExcelBaseModel> {
    private final Class<T> clazz;
    private File file;
    private File templateFile;
    private List<T> list;
    private List<ExcelMergeModel> mergeList;
    private List<ExcelHeadModel> headList;
    private HashMap<Integer, IndexedColors > bgColorMap = new HashMap<>();
    private final String exportPath = SystemConfig.fileBaseDir + File.separator + "export";

    public ExcelManager(Class<T> clazz) {
        this.clazz = clazz;
    }
    public ExcelManager<T> open(String fileName) {
        file = new File(fileName);
        if(!file.exists()){
            throw new RuntimeException(RestMsg.READ_FILE_NOT_EXISTS);
        }
        list = new ArrayList<>();
        mergeList = new ArrayList<>();
        headList = new ArrayList<>();
        return this;
    }
    public ExcelManager<T> useBgColorMap(HashMap<Integer, IndexedColors > bgColorMap){
        this.bgColorMap = bgColorMap;
        return this;
    }

    public ExcelManager<T> useTemplate(TemplateEnum template, String fileName) {
        var source = Path.of(template.getPath());
        if (!Files.exists(source)) {
            throw new CustomException(RestMsg.TEMPLATE_IS_NOT_EXISTS);
        }
        Path targetDir = Path.of(exportPath);
        if (!Files.exists(targetDir)) {
            try {
                Files.createDirectories(targetDir); // 如果目标目录不存在，则创建
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                throw new RuntimeException(e);
            }
        }
        Path targetFilePath = Path.of(targetDir.toAbsolutePath().toString(), fileName) ; //targetDir.resolve(source.getFileName());
        try {
            Files.copy(source, targetFilePath, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        file = targetFilePath.toFile();
        templateFile = new File(template.getPath());
        list = new ArrayList<>();
        mergeList = new ArrayList<>();
        headList = new ArrayList<>();
        return this;
    }

    public ExcelManager<T> readSheet(int sheetIndex) {
        DefaultListener<T> listener = new DefaultListener<>();
        listener.listenRead(list::add);
        listener.listenReadMerge(x->mergeList.add(x));
        listener.listenReadHead(head-> headList.add(head));

        EasyExcel.read(this.file, clazz, listener)
                .extraRead(CellExtraTypeEnum.MERGE)
                .sheet(sheetIndex)
                .doRead();
        return this;
    }

    public void writeSheet(List<T> data, int sheetIndex, String sheetName) {
        EasyExcel
                .write(file, clazz)
                .needHead(false)
                .withTemplate(templateFile)
                .sheet(sheetIndex, sheetName)
                .registerWriteHandler(new CellWriteHandler() {
                    @Override
                    public void afterCellDispose(CellWriteHandlerContext context) {
                        // 当前事件会在 数据设置到poi的cell里面才会回调
                        // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true
                        if (BooleanUtils.isNotTrue(context.getHead())) {
                            WriteCellData<?> cellData = context.getFirstCellData();
                            WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
                            writeCellStyle.setWrapped(true);
                        }
                    }
                }).doWrite(data);
    }
    public void writeSheetWithMergeCell(List<T> data, int sheetIndex, String sheetName, List<CellRangeAddress> mergeCellRange) {
        EasyExcel
                .write(file, clazz)
                .needHead(false)
                .withTemplate(templateFile)
                .sheet(sheetIndex, sheetName)
                .registerWriteHandler(new MergeCellStrategyHandler(mergeCellRange))
                .registerWriteHandler(new CellWriteHandler() {
                    @Override
                    public void afterCellDispose(CellWriteHandlerContext context) {
                        // 当前事件会在 数据设置到poi的cell里面才会回调
                        // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true
                        if (BooleanUtils.isNotTrue(context.getHead())) {

                            WriteCellData<?> cellData = context.getFirstCellData();
                            WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
                            writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
                            writeCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                            writeCellStyle.setBorderBottom(BorderStyle.THIN);
                            writeCellStyle.setBorderLeft(BorderStyle.THIN);
                            writeCellStyle.setBorderRight(BorderStyle.THIN);
                            writeCellStyle.setBorderTop(BorderStyle.THIN);
                            if(!bgColorMap.isEmpty()) {
                                Integer rowIndex = cellData.getRowIndex();
                                IndexedColors indexedColors = bgColorMap.get(rowIndex);
                                if(indexedColors != null) {
                                    writeCellStyle.setFillForegroundColor(indexedColors.getIndex());
                                    writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
                                }
                            }
//                            writeCellStyle.setWrapped(true);
                        }
                    }
                })
                .doWrite(data);
    }
    public void fill(List<CellRangeAddress> range, Object ...data) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ExcelWriter writer = EasyExcel
                .write(bos)
                .withTemplate(templateFile)
//                .registerWriteHandler(new MergeCellStrategyHandler(range))
                .registerWriteHandler(new CellWriteHandler() {
                    @Override
                    public void afterCellDispose(CellWriteHandlerContext context) {
                        // 当前事件会在 数据设置到poi的cell里面才会回调
                        // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true
                        if (BooleanUtils.isNotTrue(context.getHead())) {
                            WriteCellData<?> firstCellData = context.getFirstCellData();
                            WriteCellStyle writeCellStyle = firstCellData.getOrCreateStyle();
                            writeCellStyle.setWrapped(true);
                            writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
                            writeCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

                            var colIndex = context.getColumnIndex();
                            var rowIndex = context.getRowIndex();
                            if(colIndex == 6) {
                                writeCellStyle.setBorderTop(BorderStyle.THIN);
                                writeCellStyle.setBorderRight(BorderStyle.THIN);
                            }
                            if(rowIndex == 6 && colIndex == 1) {
                                writeCellStyle.setBorderTop(BorderStyle.THIN);
                            }
                        }
                    }
                })
                .build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
        for (Object obj : data) {
            writer.fill(obj, fillConfig, writeSheet);
        }
        writer.finish();

        // 后处理：动态合并单元格
        try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(bos.toByteArray()))) {
            Sheet sheet = workbook.getSheetAt(0);
            for (CellRangeAddress cellAddresses : range) {
                // 例如：合并 A~C 列
                sheet.addMergedRegion(cellAddresses);
            }
            try (FileOutputStream fos = new FileOutputStream(file)) {
                workbook.write(fos);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 将合并的单元格取消合并，并重新赋值，取值于合并的起始行列对应的值
     */
    public void unmergeCell() {
        list.forEach(item->{
            var rowNum = item.getRowNum();
            var mergedList = Linq.filter(mergeList, merge->merge.getMergeFromRowNum() <= rowNum && merge.getMergeToRowNum() >= rowNum);
            if(Linq.isEmpty(mergedList)) {
                return;
            }
            // 如果merge不为空，循环所有合并的数据
            mergedList.forEach(merge->{
                // 先获取到合并行的数据
                var mergeStartRow = merge.getMergeFromRowNum();
                // 如果当前行就是合并行的起始行，那么不需要处理
                if(mergeStartRow.equals(rowNum)) {
                    return;
                }
                // 找到起始行的数据
                T row = Linq.findFirst(list, x -> x.getRowNum().equals(mergeStartRow));
                // 再检查合并的列
                var mergeFrom = merge.getMergeFromColumnNum();
                var mergeTo = merge.getMergeToColNum();
                // 如果相等，则说明只有当前列被合并，只处理这一列即可
                if(mergeFrom.equals(mergeTo)) {
                    ExcelHeadModel headModel = Linq.findFirst(headList, head -> head.getColumnIndex().equals(mergeFrom));
                    if(headModel == null) {
                        return;
                    }
                    // 如果被合并的列头存在，通过反射获取起始行这一列的值，然后通过反射赋予给item
                    this.resetCellValue(item, row, headModel);
                } else {
                    // 不过不相等， 存在跨列合并，则逐列处理
                    for (int i = mergeFrom; i <= mergeTo; i++) {
                        int finalI = i;
                        ExcelHeadModel headModel = Linq.findFirst(headList, head -> head.getColumnIndex().equals(finalI));
                        if(headModel == null) {
                            return;
                        }
                        // 如果被合并的列头存在，通过反射获取起始行这一列的值，然后通过反射赋予给item
                        this.resetCellValue(item, row, headModel);
                    }
                }
            });
        });
    }

    /**
     * 根据列头重置current的数据
     * @param current 被重置数据的对象
     * @param readItem 读取数据的对象
     * @param head 指定列头
     */
    private void resetCellValue(T current, T readItem, ExcelHeadModel head) {
        String columnName = head.getColumnName();

        // 遍历 readItem 的所有字段
        for (Field field : readItem.getClass().getDeclaredFields()) {
            // 检查字段是否有 ExcelProperty 注解
            ExcelUnmergeIgnored unmergeIgnored = field.getAnnotation(ExcelUnmergeIgnored.class);
            // 如果忽略这个字段，则不处理
            if(unmergeIgnored != null) {
                continue;
            }
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (excelProperty != null && excelProperty.value().length > 0 && excelProperty.value()[0].equals(columnName)) {
                try {
                    // 获取 readItem 中该字段的值
                    boolean isAccessible = field.canAccess(readItem);
                    field.setAccessible(true);
                    Object value = field.get(readItem);

                    // 在 current 中找到对应字段并设置值
                    Field currentField = current.getClass().getDeclaredField(field.getName());
                    currentField.setAccessible(true);
                    currentField.set(current, value);

                    field.setAccessible(isAccessible);
                } catch (IllegalAccessException | NoSuchFieldException e) {
                    log.error(e.getMessage(),e);
                }
                break;
            }
        }
    }
}
